home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / overview / moreisbetter / mib-clients / samplesubmit / samplesubmit.c < prev   
Encoding:
C/C++ Source or Header  |  2000-06-23  |  27.1 KB  |  1,183 lines

  1. /*
  2.     File:        SampleSubmit.c
  3.  
  4.     Contains:    
  5.  
  6.     Written by:    Pete Gontier (PCG)
  7.  
  8.     Copyright:    Copyright (c) 1998 Apple Computer, Inc.
  9.  
  10.     Change History (most recent first):
  11.  
  12.          <5>     2/15/99    PCG     Carbonate
  13.          <4>     1/22/99    PCG     dunno
  14.          <3>     7/24/98    PCG     adapt to header changes
  15.          <2>     7/11/98    PCG     add AppleScript comment to AskFinderLaunchAppBySignature
  16.          <1>     7/10/98    PCG     initial checkin
  17. */
  18.  
  19. #include "MoreSetup.h"
  20.  
  21. #include <PLStringFuncs.h>
  22. #include <LowMem.h>
  23. #include <OSA.h>
  24. #include <AERegistry.h>
  25.  
  26. #include "InternetConfig.h"
  27.  
  28. #include "MoreTextUtils.h"
  29. #include "MoreDialogs.h"
  30. #include "MoreAppleEvents.h"
  31. #include "MoreToolbox.h"
  32. #include "MorePreferences.h"
  33. #include "MoreProcesses.h"
  34. #include "MoreAppearance.h"
  35. #include "MoreQuickDraw.h"
  36.  
  37. enum
  38. {
  39.     kPrefsID_SubmitterName,
  40.     kPrefsID_SubmitterEmail,
  41.     kPrefsID_ManagerName,
  42.     kPrefsID_ManagerEmail,
  43.     kPrefsID_PackageName
  44. };
  45.  
  46. enum
  47. {
  48.     kPrefsType_GenericString = 'STR '
  49. };
  50.  
  51. enum
  52. {
  53.     kDialogItemIndex_Main_None,
  54.  
  55.     kDialogItemIndex_Main_PushButton_Submit,
  56.     kDialogItemIndex_Main_PushButton_Quit,
  57.  
  58.     kDialogItemIndex_Main_GroupBox,
  59.  
  60.     kDialogItemIndex_Main_StaticText_SubmitterName,
  61.     kDialogItemIndex_Main_StaticText_SubmitterEmail,
  62.     kDialogItemIndex_Main_StaticText_ManagerName,
  63.     kDialogItemIndex_Main_StaticText_ManagerEmail,
  64.     kDialogItemIndex_Main_StaticText_PackageName,
  65.  
  66.     kDialogItemIndex_Main_EditText_SubmitterName,
  67.     kDialogItemIndex_Main_EditText_SubmitterEmail,
  68.     kDialogItemIndex_Main_EditText_ManagerName,
  69.     kDialogItemIndex_Main_EditText_ManagerEmail,
  70.     kDialogItemIndex_Main_EditText_PackageName,
  71.  
  72.     kDialogItemIndex_Main_FieldCount =
  73.         1 + (kDialogItemIndex_Main_EditText_PackageName - kDialogItemIndex_Main_EditText_SubmitterName)
  74. };
  75.  
  76. enum
  77. {
  78.     kDialogItemIndex_InternetConfigMissingKey_None,
  79.  
  80.     kDialogItemIndex_InternetConfigMissingKey_PushButton_LaunchIC,
  81.     kDialogItemIndex_InternetConfigMissingKey_PushButton_Quit,
  82.     kDialogItemIndex_InternetConfigMissingKey_StaticText_Message    
  83. };
  84.  
  85. enum
  86. {
  87.     kDialogItemIndex_PackageNameTooShort_None,
  88.  
  89.     kDialogItemIndex_PackageNameTooShort_PushButton_YesThatIsTooShortSillyMe,
  90.     kDialogItemIndex_PackageNameTooShort_StaticText_Message,
  91.     kDialogItemIndex_PackageNameTooShort_PushButton_SubmitAnyway
  92. };
  93.  
  94. enum
  95. {
  96.     kDialogItemIndex_NoReadMe_None,
  97.  
  98.     kDialogItemIndex_NoReadMe_PushButton_StartOne,
  99.     kDialogItemIndex_NoReadMe_StaticText_Message,
  100.     kDialogItemIndex_NoReadMe_PushButton_JustQuit
  101. };
  102.  
  103. enum
  104. {
  105.     kFileType_SampleCodeProfile        = 'sprf',
  106.     kCreatorCode_SampleSubmit        = 'samp',
  107.     kResType_CompiledAppleScript    = 'scpt'
  108. };
  109.  
  110. enum
  111. {
  112.     kResID_Base                            = 128,
  113.     kResID_Dialog_Profile                = 200,
  114.     kResID_StringList_Main                = kResID_Base,
  115.     kResID_StringList_ReadMe,
  116.     kResID_SearchForAppGivenSignature    = kResID_Base
  117. };
  118.  
  119. enum
  120. {
  121.     kStringIndex_ProfileName,
  122.     kStringIndex_PreferencesFileName
  123. };
  124.  
  125. enum
  126. {
  127.     kAlertIndex_AppRequiresMacOS8,
  128.     kAlertIndex_AppRequiresAppearance101,
  129.     kAlertIndex_DropFolderOnThisApp,
  130.     kAlertIndex_GenericError,
  131.     kAlertIndex_ReadMeFileWrongType,
  132.     kAlertIndex_AppRequiresInternetConfig,
  133.     kAlertIndex_MissingInternetConfigSetting,
  134.     kAlertIndex_ShortField,
  135.     kAlertIndex_EmptyField,
  136.     kAlertIndex_NoReadMeFile,
  137.     kAlertIndex_ProfileTypeOrCreatorWrong,
  138.     kAlertIndex_FreeForm
  139. };
  140.  
  141. static Boolean                gQuitting;
  142. static tStringListP            gMainStrings, gReadMeStrings;
  143. static ModalFilterUPP        gStandardModalFilterUPP;
  144.  
  145. static pascal DialogItemIndex MyAlert (UInt8 whichAlert, ConstStr255Param str1, ConstStr255Param str2)
  146. {
  147.     DialogItemIndex result = kDialogItemIndexNone;
  148.  
  149.     if (!AEInteractWithUser (kNoTimeOut,nil,nil))
  150.     {
  151.         ParamText (LMGetCurApName ( ), str1 ? str1 : "\p", str2 ? str2 : "\p", "\p");
  152.  
  153.         InitCursor ( );
  154.  
  155.         switch (whichAlert)
  156.         {
  157.             case kAlertIndex_ShortField :
  158.  
  159.                 result = CautionAlert (kResID_Base + whichAlert, gStandardModalFilterUPP);
  160.                 break;
  161.  
  162.             default :
  163.  
  164.                 result = StopAlert (kResID_Base + whichAlert, gStandardModalFilterUPP);
  165.                 break;
  166.         }
  167.  
  168.         if (MoreAssert (result != kDialogItemIndexNone))
  169.         {
  170.             EventRecord modifiers;
  171.             OSEventAvail (0,&modifiers);
  172.             if (modifiers.modifiers & optionKey)
  173.                 Debugger ( );
  174.         }
  175.     }
  176.  
  177.     return result;
  178. }
  179.  
  180. static pascal DialogItemIndex NumberStringAlert (UInt8 whichAlert, long number, ConstStr255Param auxStr)
  181. {
  182.     Str15 numString;
  183.     NumToString (number,numString);
  184.     return MyAlert (whichAlert,numString,auxStr);
  185. }
  186.  
  187. static pascal DialogItemIndex NumberAlert (UInt8 whichAlert, long number)
  188. {
  189.     return NumberStringAlert (whichAlert,number,nil);
  190. }
  191.  
  192. static pascal DialogItemIndex ErrorAlert (UInt8 whichAlert, OSStatus err)
  193. {
  194.     if (err == userCanceledErr)
  195.         return 0;
  196.     else
  197.         return NumberAlert (whichAlert,err);
  198. }
  199.  
  200. #pragma mark -
  201.  
  202. static pascal OSErr OpenApplication (const AppleEvent *event)
  203. {
  204.     OSErr err = noErr;
  205.  
  206.     Boolean isFinder;
  207.  
  208.     if (!(err = SenderIsFinder (event,&isFinder)) && isFinder)
  209.         (void) MyAlert (kAlertIndex_DropFolderOnThisApp,nil,nil);
  210.  
  211.     gQuitting = true;
  212.  
  213.     return err;
  214. }
  215.  
  216. static pascal OSErr QuitApplication (void)
  217. {
  218.     gQuitting = true;
  219.     return noErr;
  220. }
  221.  
  222. static pascal OSStatus SimpleMatchOneAlias (AliasHandle ah, UInt32 rules, FSSpecPtr fssP)
  223. {
  224.     short        count        = 1;
  225.     Boolean        dontCare;
  226.  
  227.     return MatchAlias (nil,rules,ah,&count,fssP,&dontCare,nil,nil);
  228. }
  229.  
  230. static pascal OSStatus NewResolvedAlias (AliasHandle ah, FSSpecPtr *fssPP)
  231. {
  232.     OSStatus err = noErr;
  233.  
  234.     *fssPP = (FSSpecPtr) NewPtr (sizeof (**fssPP));
  235.  
  236.     if (!*fssPP)
  237.         err = MemError ( );
  238.     else
  239.     {
  240.         UInt32 rules = kARMMountVol | kARMNoUI | kARMSearch;
  241.  
  242.         err = SimpleMatchOneAlias (ah,rules,*fssPP);
  243.  
  244.         if (err == nsvErr) // often means: "could not present UI to mount volume"
  245.         {
  246.             if (!(err = AEInteractWithUser (kNoTimeOut,nil,nil)))
  247.             {
  248.                 rules &= ~kARMNoUI;
  249.                 err = SimpleMatchOneAlias (ah,rules,*fssPP);
  250.             }
  251.         }
  252.  
  253.         if (err)
  254.             DisposePtr ((Ptr) *fssPP);
  255.     }
  256.  
  257.     return err;
  258. }
  259.  
  260. static pascal OSStatus AskFinderLaunchAppBySignature (OSType creatorCode)
  261. {
  262.     /*
  263.         on «event xxxxyyyy» appSignature
  264.             tell application "Finder" to ¬
  265.                 open (application file id appSignature)
  266.         end «event xxxxyyyy»
  267.     */
  268.  
  269.     OSStatus err = noErr;
  270.  
  271.     Handle scriptH = Get1Resource (kResType_CompiledAppleScript,kResID_SearchForAppGivenSignature);
  272.  
  273.     if (!scriptH)
  274.     {
  275.         err = ResError ( );
  276.         if (!err) err = resNotFound;
  277.     }
  278.     else
  279.     {
  280.         OSStatus            err2            = noErr;
  281.         ComponentInstance    ci                = nil;
  282.         OSAID                scriptID        = kOSANullScript;
  283.  
  284.         AppleEvent            appleEvent        = { typeNull, nil };
  285.         AEDesc                scriptData        = { typeNull, nil };
  286.         AEDesc                reply            = { typeNull, nil };
  287.  
  288.         do
  289.         {
  290.             ci = OpenDefaultComponent (kOSAComponentType, kOSAGenericScriptingComponentSubtype);
  291.  
  292.             if (ci == (ComponentInstance) badComponentInstance || ci == (ComponentInstance) badComponentSelector)
  293.             {
  294.                 err = (OSStatus) ci;
  295.                 ci = nil;
  296.                 break;
  297.             }
  298.  
  299.             MoveHHi (scriptH);
  300.             err = MemError ( );
  301.             if (err) break;
  302.             HLock (scriptH);
  303.             err = MemError ( );
  304.             if (err) break;
  305.  
  306.             err = AECreateDesc (typeOSAGenericStorage,*scriptH,GetHandleSize(scriptH),&scriptData);
  307.             if (err) break;
  308.             err = OSALoad (ci, &scriptData, kOSAModePreventGetSource, &scriptID);
  309.             if (err) break;
  310.             err = CreateSimpleAppleEvent ('xxxx','yyyy',nil,&appleEvent);
  311.             if (err) break;
  312.             err = AEPutParamPtr (&appleEvent, keyDirectObject, typeChar, &creatorCode, sizeof (creatorCode));
  313.             if (err) break;
  314.             err = OSADoEvent (ci, &appleEvent, scriptID, kOSAModeNull, &reply);
  315.  
  316.             if (err == errOSAScriptError)
  317.             {
  318.                 Size    errDescSize;
  319.                 AEDesc    errorDesc        = { typeNull, nil };
  320.                 Handle    errorDescData    = nil;
  321.  
  322.                 do
  323.                 {
  324.                     err = OSAScriptError (ci,kOSAErrorMessage,typeChar,&errorDesc);
  325.                     if (err) break;
  326.                     errDescSize = AEGetDescDataSize (&errorDesc);
  327.                     if (err) break;
  328.                     errDescSize = errDescSize > 255 ? 255 : errDescSize;
  329.  
  330.                     if (!(errorDescData = NewHandle (errDescSize + 1)))
  331.                     {
  332.                         err = MemError ( );
  333.                         break;
  334.                     }
  335.  
  336.                     MoveHHi (errorDescData);
  337.                     err = MemError ( );
  338.                     if (err) break;
  339.                     HLock (errorDescData);
  340.                     err = MemError ( );
  341.                     if (err) break;
  342.                     err = AEGetDescData (&errorDesc,typeChar,*errorDescData,errDescSize);
  343.                     if (err) break;
  344.  
  345.                     BlockMoveData (*errorDescData,1+*errorDescData,errDescSize);
  346.                     **errorDescData = errDescSize;
  347.  
  348.                     SetArrowCursor ( );
  349.  
  350.                     (void) MyAlert (kAlertIndex_FreeForm, (ConstStr255Param) *errorDescData, nil);
  351.                 }
  352.                 while (false);
  353.  
  354.                 if (errorDescData)
  355.                 {
  356.                     DisposeHandle (errorDescData);
  357.                     if (!err) err = MemError ( );
  358.                 }
  359.  
  360.                 if (errorDesc.dataHandle)
  361.                 {
  362.                     err2 = AEDisposeDesc (&errorDesc);
  363.                     if (!err) err = err2;
  364.                 }
  365.             }
  366.         }
  367.         while (false);
  368.  
  369.         if (reply.dataHandle)
  370.         {
  371.             err2 = AEDisposeDesc (&reply);
  372.             if (!err) err = err2;
  373.         }
  374.  
  375.         if (appleEvent.dataHandle)
  376.         {
  377.             err2 = AEDisposeDesc (&appleEvent);
  378.             if (!err) err = err2;
  379.         }
  380.  
  381.         if (scriptID != kOSANullScript)
  382.         {
  383.             if (MoreAssert (ci != nil))
  384.             {
  385.                 err2 = OSADispose (ci, scriptID);
  386.                 if (!err) err = err2;
  387.             }
  388.         }
  389.  
  390.         if (scriptData.dataHandle)
  391.         {
  392.             err2 = AEDisposeDesc (&scriptData);
  393.             if (!err) err = err2;
  394.         }
  395.  
  396.         if (ci)
  397.         {
  398.             err2 = CloseComponent (ci);
  399.             if (!err) err = err2;
  400.         }
  401.  
  402.         ReleaseResource (scriptH);
  403.         if (!err) err = ResError ( );
  404.     }
  405.  
  406.     return err;
  407. }
  408.  
  409. static pascal OSStatus ComplainAboutMissingInernetConfigKey (ConstStr255Param icKey)
  410. {
  411.     OSStatus err = noErr;
  412.  
  413.     DialogItemIndex index = MyAlert (kAlertIndex_MissingInternetConfigSetting,nil,icKey);
  414.  
  415.     if (kDialogItemIndex_InternetConfigMissingKey_PushButton_LaunchIC == index)
  416.     {
  417.         const OSType internetConfigSignature = 'ICAp';
  418.  
  419.         if (!(err = ShowWatchCursor ( )))
  420.             err = AskFinderLaunchAppBySignature (internetConfigSignature);
  421.     }
  422.  
  423.     return err;
  424. }
  425.  
  426. static OSStatus SetDefaultPreferenceFromInternetConfig
  427.     (ComponentInstance icc, PrefsCollection prefs, ConstStr255Param icKey, PrefsID prefsID)
  428. {
  429.     OSStatus err = noErr;
  430.  
  431.     StringPtr scratch;
  432.  
  433.     if (!(err = NewStringPtr (nil,0,&scratch)))
  434.     {
  435.         ICAttr    doNotCareAttr;
  436.         long    size = sizeof (Str255);
  437.  
  438.         err = ICCGetPref (icc,icKey,&doNotCareAttr,(Ptr)scratch,&size);
  439.  
  440.         if (err == icPrefNotFoundErr)
  441.         {
  442.             if (!(err = ComplainAboutMissingInernetConfigKey (icKey)))
  443.                 err = userCanceledErr;
  444.         }
  445.         else if (!err)
  446.         {
  447.             if (size > 1)
  448.                 err = SetPreference (prefs,kPrefsType_GenericString,prefsID,size,scratch);
  449.             else if (!(err = ComplainAboutMissingInernetConfigKey (icKey)))
  450.                 err = userCanceledErr;
  451.         }
  452.  
  453.         DisposePtr ((Ptr) scratch);
  454.         if (!err) err = MemError ( );
  455.     }
  456.  
  457.     return err;
  458. }
  459.  
  460. static pascal OSStatus CreateDefaultPrefsCollection (PrefsCollection *prefs)
  461. {
  462.     OSStatus err = noErr;
  463.  
  464.     if (!(err = NewPrefsCollection (prefs)))
  465.     {
  466.         OSStatus err2;
  467.  
  468.         ComponentInstance icc;
  469.  
  470.         err = ICCStart (&icc,kCreatorCode_SampleSubmit);
  471.  
  472.         if (err == badComponentInstance)
  473.         {
  474.             (void) MyAlert (kAlertIndex_AppRequiresInternetConfig,nil,nil);
  475.             err = userCanceledErr;
  476.         }
  477.         else if (!err)
  478.         {
  479.             if (!(err = ICCFindConfigFile (icc,nil,0)))
  480.             {
  481.                 if (!(err = SetDefaultPreferenceFromInternetConfig (icc,*prefs,kICEmail,kPrefsID_SubmitterEmail)))
  482.                 if (!(err = SetDefaultPreferenceFromInternetConfig (icc,*prefs,kICRealName,kPrefsID_SubmitterName)))
  483.                 {
  484.                     UInt8 emptyString = 0;
  485.  
  486.                     if (!(err = SetPreference (*prefs,kPrefsType_GenericString,kPrefsID_ManagerName,1,&emptyString)))
  487.                         err = SetPreference (*prefs,kPrefsType_GenericString,kPrefsID_ManagerEmail,1,&emptyString);
  488.                 }
  489.             }
  490.             err2 = ICCStop (icc);
  491.             if (!err) err = err2;
  492.         }
  493.         if (err) (void) DisposePrefsCollection (*prefs);
  494.     }
  495.  
  496.     return err;
  497. }
  498.  
  499. static pascal OSStatus PopulateProfileDialog
  500.     (PrefsCollection prefs, DialogPtr dialog, ConstStr255Param packageName)
  501. {
  502.     OSStatus err = noErr;
  503.  
  504.     StringPtr scratch;
  505.  
  506.     if (!(err = NewStringPtr (nil,0,&scratch)))
  507.     {
  508.         Size size = sizeof (Str255);
  509.  
  510.         if (!(err = GetPreference (prefs, kPrefsType_GenericString, kPrefsID_SubmitterName, size, nil, scratch)))
  511.         {
  512.             SetDialogItemString (dialog, kDialogItemIndex_Main_EditText_SubmitterName, scratch);
  513.             if (!(err = GetPreference (prefs, kPrefsType_GenericString, kPrefsID_SubmitterEmail, size, nil, scratch)))
  514.             {
  515.                 SetDialogItemString (dialog, kDialogItemIndex_Main_EditText_SubmitterEmail, scratch);
  516.  
  517.                 if (!(err = GetPreference (prefs, kPrefsType_GenericString, kPrefsID_ManagerName, size, nil, scratch)))
  518.                 {
  519.                     SetDialogItemString (dialog, kDialogItemIndex_Main_EditText_ManagerName, scratch);
  520.  
  521.                     if (!(err = GetPreference (prefs, kPrefsType_GenericString, kPrefsID_ManagerEmail, size, nil, scratch)))
  522.                     {
  523.                         SetDialogItemString (dialog, kDialogItemIndex_Main_EditText_ManagerEmail, scratch);
  524.  
  525.                         SetDialogItemString (dialog, kDialogItemIndex_Main_EditText_PackageName, packageName);
  526.                         SelectAllDialogItemText (dialog, kDialogItemIndex_Main_EditText_PackageName);
  527.                     }
  528.                 }
  529.             }
  530.         }
  531.  
  532.         DisposePtr ((Ptr) scratch);
  533.         if (!err) err = MemError ( );
  534.     }
  535.  
  536.     return err;
  537. }
  538.  
  539. static pascal OSStatus CreateAndPopulateProfileDialog
  540.     (PrefsCollection prefs, DialogPtr *dialog, ConstStr255Param packageName)
  541. {
  542.     OSStatus err = noErr;
  543.  
  544.     if (!(*dialog = MoreGetNewDialog (kResID_Dialog_Profile)))
  545.         err = nilHandleErr;
  546.     else
  547.     {    
  548.         if (!(err = PopulateProfileDialog (prefs, *dialog, packageName)))
  549.         {
  550.             if (!(err = SetDialogDefaultItem (*dialog, kDialogItemIndex_Main_PushButton_Submit)))
  551.             if (!(err = SetDialogCancelItem (*dialog, kDialogItemIndex_Main_PushButton_Quit)))
  552.                 err = SetDialogTracksCursor (*dialog, true);
  553.         }
  554.  
  555.         if (err)
  556.             DisposeDialog (*dialog);
  557.     }
  558.  
  559.     return err;
  560. }
  561.  
  562. static pascal OSStatus CreateAndPopulateProfileDialogWithDefaults
  563.     (PrefsCollection *prefs, DialogPtr *dialog, ConstStr255Param packageName)
  564. {
  565.     OSStatus err = GetNewPrefsCollection (prefs, gMainStrings->list [kStringIndex_PreferencesFileName]);
  566.  
  567.     if (err == fnfErr)
  568.         err = CreateDefaultPrefsCollection (prefs);
  569.  
  570.     if (!err)
  571.     {
  572.         err = CreateAndPopulateProfileDialog (*prefs,dialog,packageName);
  573.  
  574.         if (err)
  575.             (void) DisposePrefsCollection (*prefs);
  576.     }
  577.  
  578.     return err;
  579. }
  580.  
  581. static pascal OSStatus VerifyProfileTypeAndCreator (const FSSpec *fssP, Boolean *result)
  582. {
  583.     OSStatus err = noErr;
  584.  
  585.     FInfo fInfo;
  586.  
  587.     if (!(err = FSpGetFInfo (fssP, &fInfo)))
  588.     {
  589.         if (kFileType_SampleCodeProfile != fInfo.fdType)
  590.             *result = false;
  591.         else if (kCreatorCode_SampleSubmit != fInfo.fdCreator)
  592.             *result = false;
  593.         else
  594.             *result = true;
  595.     }
  596.  
  597.     return err;
  598. }
  599.  
  600. static pascal OSStatus OpenProfileIntoDialog
  601.     (PrefsCollection *prefs, const FSSpec *fssP, DialogPtr *dialog)
  602. {
  603.     OSStatus err = noErr;
  604.  
  605.     Boolean correct;
  606.  
  607.     if (!(err = VerifyProfileTypeAndCreator (fssP,&correct)))
  608.     {
  609.         if (!correct)
  610.         {
  611.             (void) MyAlert (kAlertIndex_ProfileTypeOrCreatorWrong,fssP->name,nil);
  612.             err = userCanceledErr;
  613.         }
  614.         else
  615.         {
  616.             err = GetNewPrefsCollectionFromFile (prefs, fssP);
  617.  
  618.             if (err == fnfErr)
  619.                 err = CreateDefaultPrefsCollection (prefs);
  620.  
  621.             if (!err)
  622.             {
  623.                 Str31 packageName;
  624.  
  625.                 if (!(err = GetPreference (*prefs, kPrefsType_GenericString, kPrefsID_PackageName,
  626.                     sizeof (packageName), nil, packageName)))
  627.                 {
  628.                     err = CreateAndPopulateProfileDialog (*prefs,dialog,packageName);
  629.                 }
  630.  
  631.                 if (err)
  632.                     (void) DisposePrefsCollection (*prefs);
  633.             }
  634.         }
  635.     }
  636.  
  637.     return err;
  638. }
  639.  
  640. static pascal OSStatus CopyDialogItemTextToPrefsCollection
  641.     (PrefsCollection prefs, PrefsID id, DialogPtr dialog, DialogItemIndex index, StringPtr scratch)
  642. {
  643.     enum { kShortEntry = 10 };
  644.  
  645.     OSStatus err = noErr;
  646.  
  647.     GetDialogItemString (dialog, index, scratch);
  648.  
  649.     if (*scratch < kShortEntry)
  650.     {
  651.         StringPtr label;
  652.  
  653.         if (!(err = NewStringPtr (nil,0,&label)))
  654.         {
  655.             GetDialogItemString (dialog, index - kDialogItemIndex_Main_FieldCount, label);
  656.  
  657.             if (!*scratch)
  658.             {
  659.                 (void) MyAlert (kAlertIndex_EmptyField,label,nil);
  660.                 err = userCanceledErr;
  661.             }
  662.             else
  663.             {
  664.                 DialogItemIndex response = NumberStringAlert (kAlertIndex_ShortField,*scratch,label);
  665.  
  666.                 if (kDialogItemIndex_PackageNameTooShort_PushButton_SubmitAnyway != response)
  667.                     err = userCanceledErr;
  668.             }
  669.  
  670.             DisposePtr ((Ptr) label);
  671.             if (!err) err = MemError ( );            
  672.         }
  673.     }
  674.  
  675.     if (!err)
  676.         err = SetPreference (prefs, kPrefsType_GenericString, id, *scratch + 1, scratch);
  677.  
  678.  
  679.     return err;
  680. }
  681.  
  682. static OSStatus GatherDialogSettings (PrefsCollection *prefs, DialogPtr dialog)
  683. {
  684.     OSStatus err = noErr;
  685.  
  686.     StringPtr scratch;
  687.  
  688.     if (!(err = NewStringPtr (nil,0,&scratch)))
  689.     {
  690.         if (!(err = NewPrefsCollection (prefs)))
  691.         {
  692.             if (!(err = CopyDialogItemTextToPrefsCollection (*prefs, kPrefsID_SubmitterName,
  693.                 dialog, kDialogItemIndex_Main_EditText_SubmitterName, scratch)))
  694.             if (!(err = CopyDialogItemTextToPrefsCollection (*prefs, kPrefsID_SubmitterEmail,
  695.                 dialog, kDialogItemIndex_Main_EditText_SubmitterEmail, scratch)))
  696.             if (!(err = CopyDialogItemTextToPrefsCollection (*prefs, kPrefsID_ManagerName,
  697.                 dialog, kDialogItemIndex_Main_EditText_ManagerName, scratch)))
  698.             if (!(err = CopyDialogItemTextToPrefsCollection (*prefs, kPrefsID_ManagerEmail,
  699.                 dialog, kDialogItemIndex_Main_EditText_ManagerEmail, scratch)))
  700.             if (!(err = CopyDialogItemTextToPrefsCollection (*prefs, kPrefsID_PackageName,
  701.                 dialog, kDialogItemIndex_Main_EditText_PackageName, scratch)))
  702.             {
  703.                 // all conditionalized out and no place to go
  704.             }
  705.  
  706.             if (err)
  707.                 (void) DisposePrefsCollection (*prefs);
  708.         }
  709.  
  710.         DisposePtr ((Ptr) scratch);
  711.         if (!err) err = MemError ( );
  712.     }
  713.  
  714.     return err;
  715. }
  716.  
  717. static OSStatus SaveProfile (PrefsCollection newPrefs, const FSSpec *profile)
  718. {
  719.     OSStatus err = FSpCreate (profile,kCreatorCode_SampleSubmit,kFileType_SampleCodeProfile,smSystemScript);
  720.  
  721.     Boolean createdItHere = false;
  722.  
  723.     if (!err)
  724.         createdItHere = true;
  725.     else if (err == dupFNErr)
  726.     {
  727.         Boolean correct;
  728.  
  729.         if (!(err = VerifyProfileTypeAndCreator (profile,&correct)))
  730.             if (!correct)
  731.                 err = dupFNErr;
  732.     }
  733.  
  734.     if (!err)
  735.     {
  736.         err = WritePrefsCollectionToFile (newPrefs,profile);
  737.         if (err && createdItHere) (void) FSpDelete (profile);
  738.     }
  739.  
  740.     return err;
  741. }
  742.  
  743. static OSStatus Submit (PrefsCollection oldPrefs, DialogPtr dialog, const FSSpec *profile)
  744. {
  745.     OSStatus err = noErr;
  746.  
  747.     PrefsCollection newPrefs;
  748.  
  749.     if (!(err = GatherDialogSettings (&newPrefs,dialog)))
  750.     {
  751.         OSStatus err2;
  752.  
  753.         if (!(err = SaveProfile (newPrefs,profile)))
  754.         {
  755.             Boolean areSame;
  756.  
  757.             if (!(err = PrefsAreSame (newPrefs,oldPrefs,&areSame)) && !areSame)
  758.                 err = WritePrefsCollection (newPrefs, gMainStrings->list [kStringIndex_PreferencesFileName]);
  759.         }
  760.  
  761.         err2 = DisposePrefsCollection (newPrefs);
  762.         if (!err) err = err2;
  763.     }
  764.  
  765.     return err;
  766. }
  767.  
  768. static pascal OSStatus AskFinderToOpenDoc (const FSSpec *fssP)
  769. {
  770.     OSStatus err = noErr;
  771.  
  772.     ProcessSerialNumber psn;
  773.  
  774.     if (!(err = FindProcessBySignature (kSignatureFinder, &psn)))
  775.     {
  776.         AppleEvent appleEvent;
  777.  
  778.         if (!(err = CreateSimpleAppleEvent (kCoreEventClass,kAEOpenDocuments,&psn,&appleEvent)))
  779.         {
  780.             OSStatus err2;
  781.  
  782.             AEDescList docList;
  783.  
  784.             if (!(err = AECreateList (nil,0,false,&docList)))
  785.             {
  786.                 AliasHandle alias;
  787.  
  788.                 if (!(err = NewAliasMinimal (fssP,&alias)))
  789.                 {
  790.                     HLockHi ((Handle) alias);
  791.                     if (!(err = MemError ( )))
  792.                     {
  793.                         if (!(err = AEPutPtr (&docList,0,typeAlias,*alias,(**alias).aliasSize)))
  794.                         {
  795.                             if (!(err = AEPutParamDesc (&appleEvent,keyDirectObject,&docList)))
  796.                             {
  797.                                 AppleEvent reply;
  798.  
  799.                                 if (!(err = SimplySendAppleEvent (&appleEvent,&reply)))
  800.                                     err = AEDisposeDesc (&reply);
  801.                             }
  802.                         }
  803.                     }
  804.                     DisposeHandle ((Handle) alias);
  805.                     if (!err) err = MemError ( );
  806.                 }
  807.                 err2 = AEDisposeDesc (&docList);
  808.                 if (!err) err = err2;
  809.             }
  810.             err2 = AEDisposeDesc (&appleEvent);
  811.             if (!err) err = err2;
  812.         }
  813.     }
  814.  
  815.     return err;
  816. }
  817.  
  818. static pascal OSStatus OfferAndStartReadMeFile (FSSpec *fssP)
  819. {
  820.     OSStatus err = noErr;
  821.  
  822.     DialogItemIndex index = MyAlert (kAlertIndex_NoReadMeFile, nil, nil);
  823.     if (kDialogItemIndex_NoReadMe_PushButton_StartOne == index)
  824.     {
  825.         if (!(err = FSMakeFSSpec (fssP->vRefNum, fssP->parID, gReadMeStrings->list [0], fssP)))
  826.             err = dupFNErr;
  827.         else if (err == fnfErr)
  828.         {
  829.             if (!(err = FSpCreate (fssP, 'ttxt', 'TEXT', smSystemScript)))
  830.             {
  831.                 err = AskFinderToOpenDoc (fssP);
  832.                 if (err) (void) FSpDelete (fssP);
  833.             }
  834.         }
  835.     }
  836.  
  837.     return err;
  838. }
  839.  
  840. static pascal OSStatus ConfirmFolderHasReadMeFile (FSSpec *fssP, Boolean *haveOne)
  841. {
  842.     OSStatus err = noErr;
  843.  
  844.     UInt16 index = gReadMeStrings->count;
  845.  
  846.     while (index--)
  847.     {
  848.         err = FSMakeFSSpec (fssP->vRefNum, fssP->parID, gReadMeStrings->list [index], fssP);
  849.  
  850.         if (err == fnfErr)
  851.             err = noErr;
  852.         else if (err)
  853.             break;
  854.         else
  855.         {
  856.             FInfo fileInfo;
  857.  
  858.             if (!(err = FSpGetFInfo (fssP,&fileInfo)))
  859.             {
  860.                 if (fileInfo.fdType != 'TEXT')
  861.                 {
  862.                     (void) MyAlert (kAlertIndex_ReadMeFileWrongType, fssP->name, nil);
  863.                     err = userCanceledErr;
  864.                 }
  865.  
  866.                 break;
  867.             }
  868.         }
  869.     }
  870.  
  871.     *haveOne = (!err && index != 0xFFFF);
  872.  
  873.     return err;
  874. }
  875.  
  876. static pascal OSStatus OpenFolder (AliasHandle ah)
  877. {
  878.     OSStatus err = noErr;
  879.  
  880.     FSSpecPtr fssP;
  881.  
  882.     if (!(err = NewResolvedAlias (ah, &fssP)))
  883.     {
  884.         Str31                packageName;
  885.         short                itemHit;
  886.         DialogPtr            dialog;
  887.         PrefsCollection        prefs                = nil;
  888.         StringPtr            name                = fssP->name;
  889.  
  890.         PLstrcpy (packageName,name);
  891.         BlockMoveData (name + 1, name + 2, *name);
  892.         ++*name; name [1] = ':';
  893.         PLstrcat (name, "\p:");
  894.         PLstrcat (name, gMainStrings->list [kStringIndex_ProfileName]);
  895.  
  896.         err = FSMakeFSSpec (fssP->vRefNum,fssP->parID,name,fssP);
  897.  
  898.         if (err == dirNFErr)
  899.         {
  900.             (void) MyAlert (kAlertIndex_DropFolderOnThisApp,nil,nil);
  901.             err = userCanceledErr;
  902.         }
  903.         else if (!err || err == fnfErr)
  904.         {
  905.             Boolean haveOne;
  906.  
  907.             if (!(err = ConfirmFolderHasReadMeFile (fssP,&haveOne)))
  908.             {
  909.                 if (!haveOne)
  910.                 {
  911.                     if (!(err = OfferAndStartReadMeFile (fssP)))
  912.                         err = userCanceledErr;
  913.                 }
  914.                 else
  915.                 {
  916.                     err = FSMakeFSSpec (fssP->vRefNum, fssP->parID, gMainStrings->list[kStringIndex_ProfileName], fssP);
  917.  
  918.                     if (!err)
  919.                         err = OpenProfileIntoDialog (&prefs,fssP,&dialog);
  920.                     else if (err == fnfErr)
  921.                         err = CreateAndPopulateProfileDialogWithDefaults (&prefs,&dialog,packageName);
  922.                 }
  923.             }
  924.         }
  925.  
  926.         if (!err)
  927.         {
  928.             OSStatus err2 = noErr;
  929.  
  930.             ShowWindow (GetDialogWindow (dialog));
  931.  
  932.             do
  933.             {
  934.                 ModalDialog (gStandardModalFilterUPP,&itemHit);
  935.  
  936.                 if (itemHit == kDialogItemIndex_Main_PushButton_Submit)
  937.                 {
  938.                     OSStatus sumbitErr = Submit (prefs,dialog,fssP);
  939.                     if (sumbitErr) itemHit = kDialogItemIndexNone;
  940.                 }
  941.             }
  942.             while (itemHit != kDialogItemIndex_Main_PushButton_Submit && itemHit != kDialogItemIndex_Main_PushButton_Quit);
  943.  
  944.             DisposeDialog (dialog);
  945.             err2 = DisposePrefsCollection (prefs);
  946.             if (!err) err = err2;
  947.         }
  948.  
  949.         DisposePtr ((Ptr) fssP);
  950.         if (!err) err = MemError ( );
  951.     }
  952.  
  953.     return err;
  954. }
  955.  
  956. static pascal OSErr OpenDocuments (const AppleEvent *event)
  957. {
  958.     OSErr err = noErr;
  959.  
  960.     AEDescList listOfDocsToOpen;
  961.  
  962.     if (!(err = AEGetParamDesc (event,keyDirectObject,typeAEList,&listOfDocsToOpen)))
  963.     {
  964.         OSStatus    err2;
  965.         long    count;
  966.  
  967.         if (!(err = AECountItems (&listOfDocsToOpen,&count)))
  968.         {
  969.             if (count != 1)
  970.             {
  971.                 Boolean isFinder;
  972.  
  973.                 if (!SenderIsFinder (event,&isFinder) && isFinder)
  974.                     (void) MyAlert (kAlertIndex_DropFolderOnThisApp,nil,nil);
  975.  
  976.                 err = paramErr;
  977.             }
  978.             else
  979.             {
  980.                 AEDesc        folder;
  981.                 AEKeyword    dontCare;
  982.  
  983.                 if (!(err = AEGetNthDesc (&listOfDocsToOpen,1,typeAlias,&dontCare,&folder)))
  984.                 {
  985.                     err = OpenFolder ((AliasHandle) folder.dataHandle);
  986.                     err2 = AEDisposeDesc (&folder);
  987.                     if (!err) err = err2;
  988.                 }
  989.             }
  990.         }
  991.  
  992.         err2 = AEDisposeDesc (&listOfDocsToOpen);
  993.         if (!err) err = err2;
  994.     }
  995.  
  996.     gQuitting = true;
  997.  
  998.     return err;
  999. }
  1000.  
  1001. static pascal OSStatus RespondToEvent (const EventRecord *event)
  1002. {
  1003.     OSStatus err = noErr;
  1004.  
  1005.     if (event->what == kHighLevelEvent)
  1006.     {
  1007.         if (!(err = AEProcessAppleEvent (event)))
  1008.         {
  1009.             ;
  1010.         }
  1011.     }
  1012.  
  1013.     return err;
  1014. }
  1015.  
  1016. static pascal OSStatus ProcessAppleEvent_Core (const AppleEvent *event, AppleEvent *, AEEventID eventID)
  1017. {
  1018.     OSStatus err = errAEEventNotHandled;
  1019.  
  1020.     switch (eventID)
  1021.     {
  1022.         case kAEOpenDocuments :
  1023.  
  1024.             err = OpenDocuments (event);
  1025.             break;
  1026.  
  1027.         case kAEOpenApplication :
  1028.  
  1029.             err = OpenApplication (event);
  1030.             break;
  1031.  
  1032.         case kAEQuitApplication :
  1033.  
  1034.             err = QuitApplication ( );
  1035.             break;
  1036.     }
  1037.  
  1038.     return err;
  1039. }
  1040.  
  1041. static pascal OSErr ProcessAppleEvent (const AppleEvent *event, AppleEvent *reply, UInt32 handlerRefcon)
  1042. {
  1043.     OSStatus err = noErr;
  1044.  
  1045.     ProcessSerialNumber        senderPSN;
  1046.     AEEventClass            eventClass;
  1047.     AEEventID                eventID;
  1048.     DescType                actualType;
  1049.     Size                    actualSize;
  1050.  
  1051.     //
  1052.     //    We'd like to be getting keyEventSourceAttr instead of keyAddressAttr,
  1053.     //    but keyEventSourceAttr is broken in at least two ways:
  1054.     //
  1055.     //        Radar 2296902 : bogus value for keyEventSourceAttr
  1056.     //        Radar 2296895 : keyEventSourceAttr is SInt16, not SInt8
  1057.     //
  1058.  
  1059.     do
  1060.     {
  1061.         err = AEGetAttributePtr (event, keyAddressAttr, typeProcessSerialNumber, &actualType, (Ptr) &senderPSN, sizeof (senderPSN), &actualSize);    
  1062.         if (err) break;
  1063.         err = AEGetAttributePtr (event, keyEventClassAttr, typeType, &actualType, (Ptr) &eventClass, sizeof (eventClass), &actualSize);    
  1064.         if (err) break;
  1065.         err = AEGetAttributePtr (event, keyEventIDAttr, typeType, &actualType, (Ptr) &eventID, sizeof (eventID), &actualSize);
  1066.         if (err) break;
  1067.     }
  1068.     while (false);
  1069.  
  1070.     if (!err)
  1071.     {
  1072.         //
  1073.         //    We don't use SameProcess because all we could do is construct a PSN
  1074.         //    thus: { kNoProcess, kCurrentProcess }. SameProcess would then produce
  1075.         //    true even if senderPSN were a real PSN, not a meta-PSN. Because we
  1076.         //    need to check for specific meta-values, we bypass SameProcess. In the
  1077.         //    vast majority of cases, SameProcess is recommended. The only reason
  1078.         //    we need to compare processes is to work around Radar 2296902, so we
  1079.         //    don't feel too bad about bypassing SameProcess, because this code
  1080.         //    will go away when Radar 2296902 gets fixed anyway.
  1081.         //
  1082.  
  1083.         Boolean directCall =
  1084.             (senderPSN.highLongOfPSN == kNoProcess) && (senderPSN.lowLongOfPSN == kCurrentProcess);
  1085.  
  1086.         if (!directCall)
  1087.             err = AESuspendTheCurrentEvent (event);
  1088.  
  1089.         if (!err)
  1090.         {
  1091.             switch (eventClass)
  1092.             {
  1093.                 case kCoreEventClass :
  1094.  
  1095.                     err = ProcessAppleEvent_Core (event,reply,eventID);
  1096.                     if (err == errAEEventNotHandled) err = noErr;
  1097.                     break;
  1098.  
  1099.                 default :
  1100.  
  1101.                     err = errAEEventNotHandled;
  1102.                     break;
  1103.             }
  1104.  
  1105.             if (err && err != errAEEventNotHandled)
  1106.             {
  1107.                 if (reply->dataHandle)
  1108.                     err = AEPutParamPtr (reply,keyErrorNumber,typeLongInteger,&err,sizeof(err));
  1109.                 else
  1110.                     err = ErrorAlert (kAlertIndex_GenericError,err);
  1111.             }
  1112.  
  1113.             if (!directCall)
  1114.             {
  1115.                 OSStatus err2 = AEResumeTheCurrentEvent (event,reply,kAENoDispatch,handlerRefcon);
  1116.                 if (!err) err = err2;
  1117.             }
  1118.         }
  1119.     }
  1120.  
  1121.     if (err && err != errAEEventNotHandled)
  1122.         err = noErr; // eat it; AppleEvent Manager won't do anything good with it
  1123.  
  1124.     return err; // err is an OSStatus, but, for the values we return, truncation is OK
  1125. }
  1126.  
  1127. static pascal OSStatus SetUpApplication (Boolean *canRun)
  1128. {
  1129.     OSStatus err = noErr;
  1130.  
  1131.     *canRun = false;
  1132.  
  1133.     if (!(err = InitMac ( )))
  1134.     {
  1135.         if (GetSystemVersion ( ) < 0x0800)
  1136.             (void) MyAlert (kAlertIndex_AppRequiresMacOS8,nil,nil);
  1137.         else if (!(err = GetStdFilterProc (&gStandardModalFilterUPP)))
  1138.         {
  1139.             if (GetAppearanceVersion ( ) < 0x0101)
  1140.                 (void) MyAlert (kAlertIndex_AppRequiresAppearance101,nil,nil);
  1141.             else
  1142.             {
  1143.                 AEEventHandlerUPP aeEventHandlerUPP = NewAEEventHandlerProc (ProcessAppleEvent);
  1144.  
  1145.                 if (!(err = AEInstallEventHandler (typeWildCard,typeWildCard,aeEventHandlerUPP,nil,false)))
  1146.                 {
  1147.                     if (!(err = GetNewStringList (kResID_StringList_Main,        &gMainStrings)))
  1148.                     if (!(err = GetNewStringList (kResID_StringList_ReadMe,        &gReadMeStrings)))
  1149.                         *canRun = true;
  1150.                 }
  1151.             }
  1152.         }
  1153.     }
  1154.  
  1155.     return err;
  1156. }
  1157.  
  1158. void main (void)
  1159. {
  1160.     OSStatus err = noErr;
  1161.  
  1162.     Boolean canRun;
  1163.  
  1164.     if (!(err = SetUpApplication (&canRun)) && canRun)
  1165.     {
  1166.         EventRecord event;
  1167.  
  1168.         gQuitting = false;
  1169.  
  1170.         do
  1171.         {
  1172.             OSStatus loopErr;
  1173.             WaitNextEvent (everyEvent, &event, -1, nil);
  1174.             loopErr = RespondToEvent (&event);
  1175.             if (loopErr) (void) ErrorAlert (kAlertIndex_GenericError,loopErr);
  1176.         }
  1177.         while (!gQuitting);
  1178.     }
  1179.  
  1180.     if (err)
  1181.         (void) ErrorAlert (kAlertIndex_GenericError,err);
  1182. }
  1183.